#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include "../ddxc.h"


int my_recv(int sockfd,void* buf,int length){
    if(buf== NULL){
        return -1;
    }
    int temp=0;
    int ftemp=0;

    while(1){

        ftemp=recv(sockfd,buf+temp,length - temp,0);//0 阻塞(发送的缓冲区满/网络阻塞);
        // printf("ftemp%d\n",ftemp);
        if(ftemp == -1){
            perror("my_send err");
            return -1;
        }
        temp+=ftemp;
        // printf("temp%d\n",temp);
        if(ftemp == 0){
            return temp;
        }
        if(temp >= length){
            break;
        }
        
    }
    return temp;
}

int my_send(int sockfd,const void* buf,int length){
    if(buf == NULL){
        return -1;
    }
    int temp=0;
    int ftemp=0;

    while(1){
        
        ftemp=send(sockfd,buf+temp,length - temp,0);//0 阻塞(发送的缓冲区满/网络阻塞);
        // printf("ftemp%d\n",ftemp);
        if(ftemp == -1){
            perror("my_send err");
            return -1;
        }
        temp+=ftemp;
        if(temp >= length){
            break;
        }
        
    }
    return temp;
}

long file_size(const char* filename){ //获取文件体积(返回文件大小)
    if(filename == NULL){
        return -1;
    }
    long temp =0;
    if(access(filename,F_OK) == 0){
        int fd_r = open(filename,O_RDONLY);
        if(fd_r == 0){
            perror("open err");
            return -1;
        }
        temp=lseek(fd_r,0,SEEK_END);

        close(fd_r);
    }else{
        return 0;
    }
    

    return temp;
}

int serializa_msg(void* node,int num){//将数值写入四个字节中（序列化）(主机到网络)
    if(node == NULL){
        return -1;
    }
    int* temp=(int*)node;
    (*temp)=htonl(num);
    return 0;
}

int serializa_msg_char(int sockfd,char* node,int num){//（序列化char）
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    (*node)=num;
    if(my_send(sockfd,node,CHAR_SIZE) == -1){//
        perror("serializa_msg_char err");
        return -1;
    }
    return 0;
}

int serializa_msg_short(int sockfd,short* node,int num){//(序列化short)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    (*node)=htons(num);
    if(my_send(sockfd,node,SHORT_SIZE) == -1){ 
        perror("send err");
        return -1;
    }
    return 0;
}

int serializa_msg_int(int sockfd,int* node,int num){//(序列化int)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    (*node)=htonl(num);
    if(my_send(sockfd,node,INT_SIZE) == -1){
        perror("send err");
        return -1;
    }
    return 0;
}

int serializa_msg_long(int sockfd,long* node,int num){//(序列化long)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    (*node)=htonl(num);
    if(my_send(sockfd,node,LONG_SIZE) == -1){ //将文件名，和已下载的字节数传到服务端
        perror("send err");
        return -1;
    }
    return 0;
}

int serializa_msg_block(int sockfd,char* node,int num){//(序列化字符串)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(num <=0){
        return -1;
    }
    int* temp=(int*)node;
    (*temp)=htonl(num);
    // if(my_send(sockfd,node,num+INT_SIZE) == -1){ 
    //     perror("send err");
    //     return -1;
    // }
    return my_send(sockfd,node,num+INT_SIZE);
}

int unserializa_msg(void* node){//(反序列化)(网络到主机)
    if(node == NULL){
        return -1;
    }
    
    int* temp=(int*)node;
    (*temp)=ntohl((*temp));
    return (*temp);
}

int unserializa_msg_char(int sockfd,char* node){//(反序列化char)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(my_recv(sockfd,node,CHAR_SIZE) != CHAR_SIZE){ 
        perror("send err");
        return -1;
    }
    return 0;
}

int unserializa_msg_short(int sockfd,short* node){//(反序列化short)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(my_recv(sockfd,node,SHORT_SIZE) != SHORT_SIZE){ //将文件名，和已下载的字节数传到服务端
        perror("send err");
        return -1;
    }
    (*node)=ntohs(*node);
    return 0;
}

int unserializa_msg_int(int sockfd,int* node){//(反序列化int)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(my_recv(sockfd,node,INT_SIZE) != INT_SIZE){ //将文件名，和已下载的字节数传到服务端
        perror("send err");
        return -1;
    }
    (*node)=ntohl(*node);
    return 0;
}

int unserializa_msg_long(int sockfd,long* node){//(反序列化long)
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(my_recv(sockfd,node,LONG_SIZE) != LONG_SIZE){ //将文件名，和已下载的字节数传到服务端
        perror("send err");
        return -1;
    }
    (*node)=ntohl(*node);
    return 0;
}

int unserializa_msg_block(int sockfd,char* node){//(反序列化字符串)//返回接收到的有效字节数
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(my_recv(sockfd,node,INT_SIZE) != INT_SIZE){ //将文件名，和已下载的字节数传到服务端
        perror("recv err");
        return -1;
    }
    
    int num=unserializa_msg(node);
        
    return my_recv(sockfd,(node+INT_SIZE),num);
}

/*
sockfd：为接收方套接字 // node:为传输数据的结构指针 //type:选择发送类型：char/short/int/long/string(流);
num:在传输string(流)的时候，num为传输的有效字节数;其他的，num为传输的具体数值;
*/
int sticky_packs_send_func(int sockfd,void* node,char type,long num){//解决沾包-发送方
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(serializa_msg_char(sockfd,node,type) == -1){//发送数据类型
        return -1;
    }
    if(type == CHAR_TYPE){
        if(serializa_msg_char(sockfd,node,(char)num) == -1){
            return -1;
        }
    }else if(type == INT_TYPE){
        if(serializa_msg_int(sockfd,node,(int)num) == -1){
            printf("send int err");
            return -1;
        }
        
    }else if(type == LONG_TYPE){
        if(serializa_msg_long(sockfd,node,num) == -1){
            return -1;
        }
        
    }else if(type == STRING_TYPE){
        
        return serializa_msg_block(sockfd,node,(int)num);
        
    }
    
    return 0;
}

/*
sockfd：为发送方套接字 // node:为传输数据的结构的指针 //type:用于接收，收到的数据类型：char/short/int/long/string(流);
*/
int sticky_packs_recv_func(int sockfd,void* node,char type){
    if(node == NULL){
        return -1;
    }
    if(sockfd <=0){
        return -1;
    }
    if(unserializa_msg_char(sockfd,&type) == -1){//发送数据类型
        return -1;
    }
    if(type == CHAR_TYPE){
        if(unserializa_msg_char(sockfd,(char*)node) == -1){
            return -1;
        }
    }else if(type == INT_TYPE){
        if(unserializa_msg_int(sockfd,(int*)node) == -1){
            return -1;
        }
        
    }else if(type == LONG_TYPE){
        if(unserializa_msg_long(sockfd,(long*)node) == -1){
            return -1;
        }
    }else if(type == STRING_TYPE){
        
        return unserializa_msg_block(sockfd,(char*)node);
    }
    
    return 0;
}


void my_close(int sockfd,int clientfd){

    if(sockfd > 0){
        close(sockfd);
    }
    if(clientfd > 0){
        close(clientfd);
    }

    return;
}

// int find_name(char* name){
//     for(int i=0;i<SEVER_BUF_SIZE;i++){
//         if(name[i] == '\0'){
//             return 0;
//         }
//     }
//     return -1;
// }

int putmsg(int clientfd,msglist node,int fd_r){
    int temp=0;
    memset(node->buf,0,SEVER_BUF_SIZE);
    temp =read(fd_r,(node->buf+INT_SIZE),node->blocksize-INT_SIZE);//读取数据
    if(temp == 0){
        printf("send complete\n");
        
        sleep(2);//等待客户端关闭
        return 0;
    }else if(temp > 0){
        sticky_packs_send_func(clientfd,node->buf,STRING_TYPE,temp);
    }else{
        return -1;
    }
    return 1;
}

int msg2_file(int sockfd,int clientfd,msglist node,char* name){
    if(sticky_packs_recv_func(clientfd,node->buf,STRING_TYPE) == -1){
        return -1;
    }

    
    node->filesize=file_size((node->buf+INT_SIZE));//获取文件大小并存入结构体中
    if(node->filesize <= 0){
        return -1;
    }
    strcpy(name,(node->buf+INT_SIZE));
    printf("%s\n",name);
    sticky_packs_send_func(clientfd,&node->filesize,INT_TYPE,node->filesize);
    
    return 0;
}

int msg4_send(int sockfd,int clientfd,msglist node,char* name){

    if(sticky_packs_recv_func(clientfd,&node->blocksize,INT_TYPE) == -1){
        return -1;
    }
    if(node->blocksize > SEVER_BUF_SIZE){
        node->blocksize = SEVER_BUF_SIZE;
    }
    if(sticky_packs_recv_func(clientfd,&node->offset,INT_TYPE) == -1){
        return -1;
    }
    printf("blocksize:%d,offset:%d\n",node->blocksize,node->offset);
    unserializa_msg(&node->filesize);
    if(node->offset >= node->filesize){
        return 0;
    }
    if(node->offset == ntohl(node->filesize)){
        return 0;
    }
    int fd_r = open(name,O_RDONLY);
    if(fd_r == -1){
        perror("open err");
        return -1;
    }
    printf("offset:%d\n",node->offset);
    lseek(fd_r,node->offset,SEEK_SET);//跳到偏移量所在位置
    while(1){
        int temp=putmsg(clientfd,node,fd_r);//发送消息
        if(temp == -1){
            close(fd_r);
            return -1;
        }else if(temp == 0){//正常关闭
            close(fd_r);
            return 0;
        }
    }
    close(fd_r);

}


int getmsg(int sockfd,msglist node,int fd_w){
    if(node == NULL){
        return -1;
    }
    int ret=sticky_packs_recv_func(sockfd,node->buf,STRING_TYPE);
    
    if(ret < 0){
        perror("recv err");
        return -1;
    }else if(ret >= 0){
        int num=(* ( (int*)node->buf ) );
        // printf("num=%d\n",num);
        // if(num <= 0){
        //     printf("recv complete\n");
        //     return 0;
        // }
        if(fd_w != NO_WRITE){
            node->filesize-=ret;
            if(write(fd_w,(node->buf+INT_SIZE),ret) == -1){
                printf("write err\n");
                return -1;
            }
            if(node->filesize <= 0){//全部数据接收完毕
                printf("recv complete\n");
                return 0;
            }
        }
        if(ret == 0){
            printf("sever Disconnect\n");
            return 0;
        }

    }
    return 1;

}

int msg1_file(int sockfd,msglist node,const char* name,int nodesize){
    if(node == NULL){
        return -1;
    }
    memset(node,0,nodesize);
    strcpy((node->buf+INT_SIZE),name);
    if(sticky_packs_send_func(sockfd,node->buf,STRING_TYPE,(strlen(name) + 1)) == -1){
        printf("err");
        return -1;
    }
 
    if(sticky_packs_recv_func(sockfd,&node->filesize,LONG_TYPE) == -1){
        return -1;
    }
    // unserializa_msg(&node->filesize);
    printf("%ld\n",node->filesize);
    if(node->filesize <= 0){
        return -1;
    }
    return 0;
}

int msg3_block(int sockfd,msglist node,const char* name){
    if(node == NULL){
        return -1;
    }
    if(sticky_packs_send_func(sockfd,&node->blocksize,INT_TYPE,CLIENT_BUF_SIZE) == -1){
        printf("send blocksize err");
        return -1;
    }
    if(sticky_packs_send_func(sockfd,&node->offset,INT_TYPE,file_size(name)) == -1){
        printf("send offset err");
        return -1;
    }
    unserializa_msg(&node->offset);
    if(node->offset >= node->filesize){
        return 0;
    }
    int fd_w = open(name,O_WRONLY | O_CREAT,0666);
    if(fd_w == -1){
        perror("open err");
        // close(sockfd);
        return -1;
    }
    printf("offset%d\n",node->offset);
    node->filesize=node->filesize-node->offset;//文件剩余大小
    lseek(fd_w,node->offset,SEEK_SET);
    int temp=0;

    while(1){
        // printf("filesize:%d\n",node->filesize);
        temp = getmsg(sockfd,node,fd_w);
        if(temp == 0){
            break;
        }else if(temp < 0){
            close(fd_w);
            return -1;
        }
    }
    close(fd_w);
    return 0;
}
